//: [Previous](@previous)

/*:
 
 * Swift 语言提供数组（Array）、集合（Set）和字典（Dictionary）三种基本的集合类型用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。
 
 * Swift 中的数组、集合和字典必须明确其中保存的键和值类型，这样就可以避免插入一个错误数据类型的值。同理，对于获取到的值你也可以放心，其数据类型是确定的。
 
 * Swift 的数组、集合和字典类型被实现为泛型集合。
 
 * 如果创建一个数组、集合或字典并且把它分配成一个变量，这个集合将会是可变的。这意味着可以在创建之后添加、修改或者删除数据项。
 
 * 如果把数组、集合或字典分配成常量，那么它就是不可变的，它的大小和内容都不能被改变。
 
 * 在不需要改变集合的时候创建不可变集合是很好的实践。这样做便于你理解自己的代码，也能让 Swift 编译器优化集合的性能。
 
 #### 数组（Arrays）
 
 * 数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
 
 * Swift 的 Array 类型被桥接到 Foundation 中的 NSArray 类。
 
 * Swift 中数组的完整写法为 Array<Element>，其中 Element 是这个数组中唯一允许存在的数据类型。也可以使用像 [Element] 这样的简单语法。尽管两种形式在功能上是一样的，但是推荐较短的那种，而且在本文中都会使用这种形式来使用数组。
 
 * 你可以使用构造语法来创建一个由特定数据类型构成的空数组
 
 * Swift 中的 Array 类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。可以把准备加入新数组的数据项数量（count）和适当类型的初始值（repeating）传入数组构造函数
 
 * 你可以使用加法操作符（+）来组合两个已存在的相同类型数组。新数组的数据类型会从两个数组的数据类型中推断出来
 
 * 你可以使用数组字面量来进行数组构造，这是一种用一个或者多个数值构造数组的简单方法。数组字面量是一系列由逗号分割并由方括号包含的数值。由于 Swift 的类型推断机制，当你用字面量构造拥有相同类型值数组的时候，不必把数组的类型定义清楚。
 
 * 你可以通过数组的方法和属性来访问和修改数组，或者使用下标语法。
 
 * 可以使用加法赋值运算符（+=）直接将另一个相同类型数组中的数据添加到该数组后面。
 
 * 当你使用下标语法，所使用的下标必须是有效的。例如，试图通过 shoppingList[shoppingList.count] = "Salt" 在数组的最后添加一项，将产生一个运行时错误。
 
 * 还可以利用下标来一次改变一系列数据值，即使新数据和原有数据的数量是不一样的。
 
 * 如果你试图通过越界索引来执行访问或者修改数据的操作，会引发一个运行时错误。此时可以使用索引值和数组的 count 属性进行比较来在使用该索引之前检验其是否有效。除了当 count 等于 0 时（说明这是个空数组），最大索引值一直是 count - 1，因为数组都是零起索引。
 
 * 如果你只想把数组中的最后一项移除，可以使用 removeLast() 方法而不是 remove(at:) 方法来避免需要获取数组的 count 属性。
 
 * 你可以使用 for-in 循环来遍历数组中所有的数据项：
 
 * 如果同时需要每个数据项的值和索引值，可以使用 enumerated() 方法来进行数组遍历。enumerated() 返回一个由索引值和数据值组成的元组数组。
 */

import Foundation

var someInts: [Int] = []
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印“someInts is of type [Int] with 0 items.”
someInts.append(3)
// someInts 现在包含一个 Int 值
someInts = []
// someInts 现在是空数组，但是仍然是 [Int] 类型的。

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一种 [Double] 数组，等价于 [0.0, 0.0, 0.0]
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles 被推断为 [Double]，等价于 [2.5, 2.5, 2.5]

var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double]，等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

var shoppingList = ["Eggs", "Milk"]
// shoppingList 已经被构造并且拥有两个初始项。
print("The shopping list contains \(shoppingList.count) items.")

// 输出“The shopping list contains 2 items.”（这个数组有2个项）
if shoppingList.isEmpty {
    print("The shopping list is empty.")
} else {
    print("The shopping list is not empty.")
}
// 打印“The shopping list is not empty.”（shoppinglist 不是空的）

shoppingList.append("Flour")
// shoppingList 现在有3个数据项，似乎有人在摊煎饼

shoppingList += ["Baking Powder"]
// shoppingList 现在有四项了

shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 现在有七项了

var firstItem = shoppingList[0]
// 第一项是“Eggs”

shoppingList[0] = "Six eggs"
// 其中的第一项现在是“Six eggs”而不是“Eggs”

shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 现在有6项

shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 现在有7项

// 现在是这个列表中的第一项是“Maple Syrup”
let mapleSyrup = shoppingList.remove(at: 0)
// 索引值为0的数据项被移除
// shoppingList 现在只有6项，而且不包括 Maple Syrup

// mapleSyrup 常量的值等于被移除数据项“Maple Syrup”
firstItem = shoppingList[0]
// firstItem 现在等于“Six eggs”

let apples = shoppingList.removeLast()
// 数组的最后一项被移除了
// shoppingList 现在只有5项，不包括 Apples
// apples 常量的值现在等于字符串“Apples”

for item in shoppingList {
    print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas

for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas

/*:
 #### 集合（Sets）
 
 * 集合用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
 
 *  Swift 的 Set 类型被桥接到 Foundation 中的 NSSet 类。
 
 * 一个类型为了存储在集合中，该类型必须是可哈希化的——也就是说，该类型必须提供一个方法来计算它的哈希值。
 
 * 一个哈希值是 Int 类型的，相等的对象哈希值必须相同，比如 a == b,因此必须 a.hashValue == b.hashValue。
 
 * Swift 的所有基本类型（比如 String、Int、Double 和 Bool）默认都是可哈希化的，可以作为集合值的类型或者字典键的类型。没有关联值的枚举成员值（在 枚举 有讲述）默认也是可哈希化的。
 
 * 你可以使用自定义的类型作为集合值的类型或者是字典键的类型，但需要使自定义类型遵循 Swift 标准库中的 Hashable 协议。遵循 Hashable 协议的类型需要提供一个类型为 Int 的可读属性 hashValue。因为 Hashable 协议遵循 Equatable 协议，所以遵循该协议的类型也必须提供一个“是否相等”运算符（==）的实现。
 
 * Swift 中的集合类型被写为 Set<Element>，这里的 Element 表示集合中允许存储的类型。和数组不同的是，集合没有等价的简化形式。
 
 * 你可以通过构造器语法创建一个特定类型的空集合。
 
 * 如果上下文提供了类型信息，比如作为函数的参数或者已知类型的变量或常量，你可以通过一个空的数组字面量创建一个空的集合
 * 你可以使用数组字面量来构造集合，相当于一种简化的形式将一个或者多个值作为集合元素。
 
 * 你可以通过集合的属性和方法来对其进行访问和修改。
 
 * 你可以高效地完成集合的一些基本操作，比如把两个集合组合到一起，判断两个集合共有元素，或者判断两个集合是否全包含，部分包含或者不相交。
 
 */

var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// 打印“letters is of type Set<Character> with 0 items.”

letters.insert("a")
// letters 现在含有1个 Character 类型的值
letters = []
// letters 现在是一个空的 Set，但是它依然是 Set<Character> 类型

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被构造成含有三个初始值的集合
// 由于类型推断，<String>可以省略，

print("I have \(favoriteGenres.count) favorite music genres.")
// 打印“I have 3 favorite music genres.”

if favoriteGenres.isEmpty {
    print("As far as music goes, I'm not picky.")
} else {
    print("I have particular music preferences.")
}
// 打印“I have particular music preferences.”

favoriteGenres.insert("Jazz")
// favoriteGenres 现在包含4个元素

if let removedGenre = favoriteGenres.remove("Rock") {
    print("\(removedGenre)? I'm over it.")
} else {
    print("I never much cared for that.")
}
// 打印“Rock? I'm over it.”

if favoriteGenres.contains("Funk") {
    print("I get up on the good foot.")
} else {
    print("It's too funky in here.")
}
// 打印“It's too funky in here.”

for genre in favoriteGenres {
    print("\(genre)")
}
// Classical
// Jazz
// Hip hop

for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// Classical
// Hip hop
// Jazz

let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]

oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]

houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true

/*:
 #### 字典
 
 * 字典是一种无序的集合，它存储的是键值对之间的关系，其所有键的值需要是相同的类型，所有值的类型也需要相同。
 
 * Swift 的 Dictionary 类型被桥接到 Foundation 的 NSDictionary 类。
 
 * Swift 的字典使用 Dictionary<Key, Value> 定义，其中 Key 是一种可以在字典中被用作键的类型，Value 是字典中对应于这些键所存储值的数据类型。
 
 * 你也可以用 [Key: Value] 这样简化的形式去表示字典类型。
 
 * 你可以像数组一样使用构造语法创建一个拥有确定类型的空字典：
 
 * 一个字典的 Key 类型必须遵循 Hashable 协议，就像 Set 的值类型。
 
 * 你可以通过字典的方法和属性来访问和修改字典，或者通过使用下标语法。
 
 * Swift 的 Dictionary 是无序集合类型。为了以特定的顺序遍历字典的键或值，可以对字典的 keys 或 values 属性使用 sorted() 方法。
 
 */

var namesOfIntegers: [Int: String] = [:]
// namesOfIntegers 是一个空的 [Int: String] 字典

namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典

var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// 类型推断，[String: String]可以省略

print("The dictionary of airports contains \(airports.count) items.")
// 打印“The dictionary of airports contains 2 items.”（这个字典有两个数据项）

if airports.isEmpty {
    print("The airports dictionary is empty.")
} else {
    print("The airports dictionary is not empty.")
}
// 打印“The airports dictionary is not empty.”

airports["LHR"] = "London"
// airports 字典现在有三个数据项

airports["LHR"] = "London Heathrow"
// “LHR”对应的值被改为“London Heathrow”

if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("The old value for DUB was \(oldValue).")
}
// 输出“The old value for DUB was Dublin.”

if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName).")
} else {
    print("That airport is not in the airports dictionary.")
}
// 打印“The name of the airport is Dublin Airport.”

airports["APL"] = "Apple Internation"
// “Apple Internation”不是真的 APL 机场，删除它
airports["APL"] = nil
// APL 现在被移除了

if let removedValue = airports.removeValue(forKey: "DUB") {
    print("The removed airport's name is \(removedValue).")
} else {
    print("The airports dictionary does not contain a value for DUB.")
}
// 打印“The removed airport's name is Dublin Airport.”

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR

for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow

let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]

let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]

//: [Next](@next)
